package cn.xy.ymcc.service.impl;

import cn.xy.ymcc.doc.CourseDoc;
import cn.xy.ymcc.domain.*;
import cn.xy.ymcc.dto.CourseDto;
import cn.xy.ymcc.feign.CourseSearchFeignClient;
import cn.xy.ymcc.feign.MediaFileFeignClient;
import cn.xy.ymcc.mapper.CourseMapper;
import cn.xy.ymcc.result.JSONResult;
import cn.xy.ymcc.service.*;
import cn.xy.ymcc.util.AssertUtil;
import cn.xy.ymcc.util.StrUtils;
import cn.xy.ymcc.vo.CourseDetailInfoVo;
import cn.xy.ymcc.vo.CourseItemVo;
import cn.xy.ymcc.vo.CourserOrderVo;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xy.cao
 * @since 2022-07-21
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {

    @Autowired
    private ITeacherService teacherService;
    @Autowired
    private ICourseDetailService courseDetailService;
    @Autowired
    private ICourseResourceService courseResourceService;
    @Autowired
    private ICourseTeacherService courseTeacherService;
    @Autowired
    private ICourseSummaryService courseSummaryService;
    @Autowired
    private ICourseMarketService courseMarketService;
    @Autowired
    private ICourseTypeService courseTypeService;

    @Autowired
    private ICourseChapterService courseChapterService;
    @Autowired
    private CourseSearchFeignClient courseSearchFeignClient;
    @Autowired
    private MediaFileFeignClient mediaFileFeignClient;

    @Override
    public void save(CourseDto dto) {
        Course course = dto.getCourse();
        CourseDetail courseDetail = dto.getCourseDetail();
        CourseMarket courseMarket = dto.getCourseMarket();
        CourseResource courseResource = dto.getCourseResource();
        List<Long> teacharIds = dto.getTeacharIds();

        //1、参数空校验，在实例类头部注解校验
        //2、校验课程是否注册
        Course courseTemp = selectByName(course.getName());
        AssertUtil.isNull(courseTemp,"课程已经被注册");

        //3、保存数据
        //先将状态、讲师、添加课程初始化
        //3.1 保存课程
        course.setStatus(Course.STATUS_OFFLINE);
        course.setLoginId(1L);
        course.setLoginUserName("yhptest1");
        List<Teacher> teachers = teacherService.selectBatchIds(teacharIds);

        //jdk1.8流式编程
        String collect = teachers.stream().map(Teacher::getName).collect(Collectors.joining(","));

//        StringBuilder sb = new StringBuilder();
//        teachers.forEach(teacher -> {
//            sb.append(teacher.getName()).append(",");
//        });
        course.setTeacherNames(collect);
        insert(course);

        //3.2 保存课程详情
        //课程详情id要与课程id一致
        courseDetail.setId(course.getId());
        courseDetailService.insert(courseDetail);

        //3.3 保存课程销售
        //销售id与课程id一致
        courseMarket.setId(course.getId());
        courseMarketService.insert(courseMarket);

        //3.4 保存课程讲师
        teacharIds.forEach(id->{
            CourseTeacher courseTeacher = new CourseTeacher();
            courseTeacher.setCourseId(course.getId());
            courseTeacher.setId(id);
            courseTeacherService.insert(courseTeacher);
        });

        //3.5 保存课程资源
        courseResource.setCourseId(course.getId());
        courseResourceService.insert(courseResource);

        //3.6 初始化课程统计
        CourseSummary courseSummary = new CourseSummary();
        courseSummary.setId(course.getId());
        courseSummaryService.insert(courseSummary);

        //3.7 课程类型表中的课程数加1
        CourseType type = courseTypeService.selectById(course.getCourseTypeId());
        type.setTotalCount(type.getTotalCount() + 1);
        courseTypeService.updateById(type);
    }



    private Course selectByName(String name) {
        Wrapper<Course> tWrapper = new EntityWrapper<Course>();
        tWrapper.eq("name",name);
        return selectOne(tWrapper);

    }

    /**
     * 课程发布
     * @param courseId
     */
    @Override
    @Transactional
    public void onLineCourse(Long courseId) {
        //校验前端传来的courseId是否为空
        AssertUtil.isNotNull(courseId,"未选中课程");
        //1、校验课程是否存在
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,"课程查询错误");
        //2、修改课程状态为上架
        course.setStatus(Course.STATUS_ONFLINE);
        course.setOnlineTime(new Date());//设置上架时间
        updateById(course);
        //3、保存课程相关内容到ES
        //3.1、为service-search编写feign接口，ymcc-api-search
        //3.2、在课程微服务中依赖ymcc-api-search,并在application.yml中配置熔断降级，并在CourseStart类上添加注解@EnableFeignClients
        //3.3、使用feign远程调用search,保存课程到ES中

        CourseType type = courseTypeService.selectById(course.getCourseTypeId());//此数据中存在doc的部分数据，所以需要查询出来，
        CourseMarket courseMarket = courseMarketService.selectById(courseId);//此数据中存在doc的部分数据，所以需要查询出来，
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);//此数据中存在doc的部分数据，所以需要查询出来，
        CourseDoc doc = new CourseDoc();
        //将上面的数据拷贝到doc
        BeanUtils.copyProperties(course,doc);
        BeanUtils.copyProperties(courseMarket,doc);
        BeanUtils.copyProperties(courseSummary,doc);
        doc.setCourseTypeName(type.getName());
        String chargeName = courseMarket.getCharge() == 1 ? "免费" : "收费";
        doc.setChargeName(chargeName);
        //设置需要保存到ES中的字段，根据需求变化，

        JSONResult result = courseSearchFeignClient.saveCourse(doc);
        AssertUtil.isTrue(result.isSuccess(),"课程发布失败");


    }

    /**
     * 课程下架
     * @param courseId
     */
    @Override
    public void offLineCourse(Long courseId) {
        //校验前端传来的courseId是否为空
        AssertUtil.isNotNull(courseId,"未选中课程");

        //1、校验课程是否存在
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,"课程查询错误");

        //2.判断状态是否是下架状态 下架状态就不允许重复操作
        boolean onLine = course.getStatus() == Course.STATUS_ONFLINE;
        AssertUtil.isTrue(onLine,"课程状态不满足。");

        //2、修改课程状态为下架
        course.setStatus(Course.STATUS_OFFLINE);
        updateById(course);

        //3、调用feign远程删除ES中对应的数据
        JSONResult jsonResult = courseSearchFeignClient.offLineCourse(courseId);
        //校验成功与否
        AssertUtil.isTrue(jsonResult.isSuccess(),"课程下架失败");
    }

    /**
     * 查询详情页
     * @param courseId
     * @return
     */
    @Override
    public CourseDetailInfoVo searchCourseDetail(Long courseId) {
        //入参判断
        AssertUtil.isNotNull(courseId,"请求非法");
        //根据课程ID查课程
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,"请求非法");
        boolean onLine = course.getStatus() == Course.STATUS_ONFLINE;
        //判断课程是否上线
        AssertUtil.isTrue(onLine,"请求非法");
        //因为id强一致，所以直接根据id查询
        CourseDetail courseDetail = courseDetailService.selectById(courseId);
        CourseMarket courseMarket = courseMarketService.selectById(courseId);
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);

        //因为查询老师有一张中间表，所以直接注入中间表业务层，设置查询条件，
        Wrapper<CourseTeacher> ctwrapper = new EntityWrapper<>();
        ctwrapper.eq("course_id",courseId);
        List<CourseTeacher> courseTeachers = courseTeacherService.selectList(ctwrapper);
        //使用流式编程得到老师的ID
        List<Long> teacherIds = courseTeachers.stream().map(teacher -> teacher.getTeacherId()).collect(Collectors.toList());
        //根据ID査老师
        List<Teacher> teachers = teacherService.selectBatchIds(teacherIds);
        //查询章节
        Wrapper<CourseChapter> ccwrapper = new EntityWrapper<>();
        ccwrapper.eq("course_id",courseId);
        List<CourseChapter> chapters = courseChapterService.selectList(ccwrapper);

        //通过feign去调用media微服务查询章节对应的视频
        chapters.forEach(chapter->{
            JSONResult jsonResult = mediaFileFeignClient.searchMediaFiles(chapter.getId());
            if(jsonResult.isSuccess() && jsonResult.getData() != null){
                List<MediaFile> fileList = JSONObject.parseArray(jsonResult.getData().toString(), MediaFile.class);
                chapter.getMediaFiles().addAll(fileList);
            }
        });
        //将数据添加到对象
        CourseDetailInfoVo vo = new CourseDetailInfoVo();
        vo.setCourse(course);
        vo.setCourseChapters(chapters);
        vo.setCourseMarket(courseMarket);
        vo.setCourseSummary(courseSummary);
        vo.setCourseDetail(courseDetail);
        vo.setTeachers(teachers);
        return vo;

    }

    /**
     * 查询订单页数据
     */
    @Override
    public CourserOrderVo courserOrderVo(String courseIds) {
        //判断参数
        AssertUtil.isNotNull(courseIds,"没有可购买的课程");
        //拆分传来的字符串并将之合并为集合
        List<Long> ids = StrUtils.splitStr2LongArr(courseIds);
        //将list转为set，用以确保没有重复id。
        Set<Long> setIds = ids.stream().collect(Collectors.toSet());

        //判断重复ID
        //使用长度判断
        boolean idSize = ids.size() == setIds.size();
        AssertUtil.isTrue(idSize,"请求失败！");
        //根据每个课程Id查询课程和销售表
        List<CourseItemVo> courseInfos = new ArrayList<>();
        BigDecimal totalAmount = new BigDecimal(0);  //总金额对象
        //遍历课程id
        for (Long id : setIds) {
            //根据课程id查课程
            Course course = selectById(id);
            //数据判断
            AssertUtil.isNotNull(course,"请求失败！");
            //为防止有人在地址栏输入课程id，获取到我们未上线的课程，所以需要添加判断查询到的课程为上线，才能进行下一步。
            boolean online = course.getStatus() == Course.STATUS_ONFLINE;
            AssertUtil.isTrue(online,"请求非法");

            //通过课程id查询销售表
            CourseMarket courseMarket = courseMarketService.selectById(id);
            //判断数据
            AssertUtil.isNotNull(courseMarket,"请求失败");
            //将查询到的课程表数据和课程销售表数据封装
            CourseItemVo courseItemVo = new CourseItemVo(course, courseMarket);
            //将每行数据添加到订单页数据中
            courseInfos.add(courseItemVo);
            //bigdicemal直接使用add添加总金额
            totalAmount = totalAmount.add(courseMarket.getPrice());
        }
        //封装订单页对象并返回
        return new CourserOrderVo(courseInfos,totalAmount);
    }

}
